﻿using FPC.Offer.IRepository;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace FPC.Offer.Repository
{
    public class BaseRepository<T> : IRepository<T> where T : class
    {
        /// <summary>
        /// 通过会话工厂获取的是当前线程内部的上下文实例，而且保证了线程内上下文唯一
        /// </summary>
        protected readonly DbContext DbContext = ContextFactory.CreateDbContext();

        /// <summary>
        /// 根据过滤条件，获取记录
        /// </summary>
        /// <param name="exp">lambda 表达式条件 The exp.</param>
        public IQueryable<T> Find(Expression<Func<T, bool>> exp = null)
        {
            var dbSet = DbContext.Set<T>().AsQueryable();
            if (exp != null)
                dbSet = dbSet.Where(exp).AsQueryable();
            return dbSet;
        }

        /// <summary>
        /// 判断是否存在
        /// </summary>
        /// <param name="exp">lambda 表达式条件</param>
        /// <returns></returns>
        public bool IsExist(Expression<Func<T, bool>> exp)
        {
            return DbContext.Set<T>().Any(exp);
        }

        /// <summary>
        /// 查找单条数据
        /// </summary>
        /// <param name="exp">lambda 表达式条件</param>
        /// <returns></returns>
        public T FindSingle(Expression<Func<T, bool>> exp)
        {
            return DbContext.Set<T>().AsNoTracking().FirstOrDefault(exp);
        }

        /// <summary>
        /// 查询所有
        /// </summary>
        /// <returns></returns>
        public List<T> Select()
        {
            return Find(null).ToList();
        }

        /// <summary>
        /// 通过条件查询满足所有
        /// </summary>
        /// <param name="exp">lambda 表达式条件</param>
        /// <returns></returns>
        public List<T> Select(Expression<Func<T, bool>> exp)
        {
            return Find(exp).ToList();
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <param name="pageIndex">页序号（从1开始）</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="totalSize">总条数</param>
        /// <returns></returns>
        public List<T> SelectPage(int pageIndex, int pageSize, out long totalSize)
        {
            return SelectPage(pageIndex, pageSize, null, out totalSize);
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <param name="pageIndex">页序号（从1开始）</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="exp">lambda 表达式条件</param>
        /// <param name="totalSize">总条数</param>
        /// <returns></returns>
        public List<T> SelectPage(int pageIndex, int pageSize, Expression<Func<T, bool>> exp, out long totalSize)
        {
            var dbSet = DbContext.Set<T>().AsQueryable();
            if (exp != null)
                dbSet = dbSet.Where(exp);
            dbSet.Take((pageIndex - 1) * pageSize).Skip(pageSize);
            if (exp != null)
                totalSize = dbSet.Count(exp);
            else
                totalSize = dbSet.Count();
            return dbSet.ToList();
        }     
        /// <summary>
        /// 分页
        /// </summary>
        /// <param name="pageIndex">页序号（从1开始）</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="exp">lambda 表达式Where条件</param>
        /// <param name="order">lambda 表达式排序条件</param>
        /// <param name="orderType">true 是正序 false 则相反</param>
        /// <param name="totalSize">总条数</param>
        /// <returns></returns>
        public List<T> SelectPageOrder(int pageIndex, int pageSize, Expression<Func<T, bool>> exp, Expression<Func<T, object>> order, bool orderType, out long totalSize)
        {

            var dbSet = DbContext.Set<T>().AsQueryable();
            if (exp != null)
                dbSet = dbSet.Where(exp);

            if (orderType)
            {
                if (order != null)
                    dbSet = dbSet.OrderBy(order);
            }
            else
            {
                if (order != null)
                    dbSet = dbSet.OrderByDescending(order);
            }

            dbSet.Take((pageIndex - 1) * pageSize).Skip(pageSize);
            if (exp != null)
                totalSize = dbSet.Count(exp);
            else
                totalSize = dbSet.Count();
            return dbSet.ToList();
        }

        /// <summary>
        /// 通用单实体添加
        /// </summary>
        /// <param name="entity">实体</param>
        public int Add(T entity)
        {
            //方法一
            //DbContext.Set<T>().Add(entity);
            //方法二
            DbContext.Entry<T>(entity).State = EntityState.Added;
            return SaveChanges();
        }

        /// <summary>
        /// 通用单实体批量添加
        /// </summary>
        /// <param name="entities">实体集合</param>
        public int Add(List<T> entities)
        {
            foreach (var item in entities)
            {
                DbContext.Entry<T>(item).State = EntityState.Added;
            }
            return SaveChanges();
        }

        /// <summary>
        /// 通用单实体修改
        /// </summary>
        /// <param name="entity">实体</param>
        public int Update(T entity)
        {
            //DbContext.Set<T>().Attach(entity);
            DbContext.Entry<T>(entity).State = EntityState.Modified;
            return SaveChanges();
        }

        /// <summary>
        /// 通用单实体删除
        /// </summary>
        /// <param name="entity">实体</param>
        public int Delete(T entity)
        {
            DbContext.Set<T>().Remove(entity);
            //DbContext.Entry<T>(entity).State = EntityState.Deleted;
            return SaveChanges();
        }


        /// <summary>
        /// DBContext上下文中的缓存与数据库进行同步保存
        /// </summary>
        public int SaveChanges()
        {
            try
            {
                return DbContext.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                throw new Exception(e.EntityValidationErrors.First().ValidationErrors.First().ErrorMessage);
            }
        }       
    }
}
